/*
 * $Id: TestADIFValue.m 71 2009-06-18 04:16:30Z jon@bway.net $
 * Chalk
 *
 * Created 5/24/09
 * Copyright Jon Gordon
 * 
 * Permission is hereby granted, free of charge, to any person obtaining 
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including 
 * without limitation the rights to use, copy, modify, merge, publish, 
 * distribute, sublicense, and/or sell copies of the Software, and to 
 * permit persons to whom the Software is furnished to do so, subject to 
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#import "TestADIFValue.h"

#import "QEADIF.h"
#import "QEADIFLocation.h"
#import "QEADIFValue.h"

@implementation TestADIFValue
#define AL_VALID_SRC_1 @"DXCC,WAS"
#define AL_VALID_1_1   @"DXCC"
#define AL_VALID_1_2   @"WAS"
#define AL_VALID_SRC_2 @"MARATHON"

#define AL_CORRECTABLE_SRC_1 @"DXCC, WAS     ,  MARATHON "
#define AL_CORRECTED_SRC_1 @"DXCC,WAS,MARATHON"

#define AL_INVALID_SRC_1 @""
#define AL_INVALID_SRC_2 @"DXCC,,WAS"
#define AL_INVALID_SRC_3 @"DXCC,SSB,WAS"
#define AL_INVALID_SRC_4 @",DXCC"
- (void)testAwardListValue
{
    NSString *src = nil;
    
    NSArray *goodValues = [NSArray arrayWithObjects:AL_VALID_SRC_1, AL_VALID_SRC_2, nil];
    for (NSString *base in goodValues) {
        src = base;
        STAssertTrue([QEADIFValue isValidAwardListSrc:&src],
                     @"Failed to recognize \"%@\" as valid source for award list.",
                     base);
        STAssertEqualObjects(src, base,
                             @"Modified correct Award List source: was \"%@\"; is \"%@\".",
                             base, src);
    } // for
    
    src = AL_CORRECTABLE_SRC_1;
    STAssertTrue([QEADIFValue isValidAwardListSrc:&src],
                 @"Failed to recognize \"%@\" as correctable source for award list.",
                 AL_CORRECTABLE_SRC_1);
    STAssertEqualObjects(src, AL_CORRECTED_SRC_1,
                         @"Failed to correct correctable Award List source: was \"%@\"; is \"%@\"; should be \"%@\".",
                         AL_CORRECTABLE_SRC_1, src, AL_CORRECTED_SRC_1);
    
    NSArray *badValues = [NSArray arrayWithObjects:AL_INVALID_SRC_1, AL_INVALID_SRC_2, 
                          AL_INVALID_SRC_3, AL_INVALID_SRC_4, nil];
    for (NSString *base in badValues) {
        src = base;
        STAssertFalse([QEADIFValue isValidAwardListSrc:&src],
                     @"Failed to recognize \"%@\" as invalid source for award list.",
                     base);
        STAssertEqualObjects(src, base,
                             @"Modified uncorrectable Award List source: was \"%@\"; is \"%@\".",
                             base, src);
    } // for
    
    QEADIFValue *v = [QEADIFValue awardListFromString:AL_VALID_SRC_1];
    STAssertNotNil(v,
    @"Could not create Award List value from valid source \"%@\".",
                   AL_VALID_SRC_1);
    if (nil != v) {
        STAssertEqualObjects([v stringValue], AL_VALID_SRC_1,
        @"String value of Award List from valid source \"%@\" is \"%@\".",
        AL_VALID_SRC_1,
                             [v stringValue]);
        NSArray *awards = [v value];
        STAssertNotNil(awards,
                       @"Valid Award List returns nil list of awards.");
        if (nil != awards) {
            STAssertTrue(2 == [awards count],
            @"list of awards should have 2 elements; actually has %d elements.",
                           [awards count]);
            if (2 <= [awards count]) {
                STAssertEqualObjects([awards objectAtIndex:0], AL_VALID_1_1,
                                     @"first award in list is \"%@\"; should be \"%@\".",
                                     [awards objectAtIndex:0], AL_VALID_1_1);
                STAssertEqualObjects([awards objectAtIndex:1], AL_VALID_1_2,
                                     @"first award in list is \"%@\"; should be \"%@\".",
                                     [awards objectAtIndex:1], AL_VALID_1_2);
            } // if
        } // if
    } // if
    
    NSArray *srcAwards = [NSArray arrayWithObjects:AL_VALID_1_1, AL_VALID_1_2, nil];
    v = [QEADIFValue awardListFromArray:srcAwards];
    STAssertNotNil(v,
                   @"Could not create Award List value from source array.");
    if (nil != v) {
        STAssertEqualObjects([v stringValue], AL_VALID_SRC_1,
                             @"String value of Award List from source array is \"%@\"; should be \"%@\".",
                             [v stringValue],
                             AL_VALID_SRC_1);
        NSArray *awards = [v value];
        STAssertNotNil(awards,
                       @"Valid Award List returns nil list of awards.");
        if (nil != awards) {
            STAssertTrue(2 == [awards count],
                           @"list of awards should have 2 elements; actually has %d elements.",
                           [awards count]);
            if (2 <= [awards count]) {
                STAssertEqualObjects([awards objectAtIndex:0], AL_VALID_1_1,
                                     @"first award in list is \"%@\"; should be \"%@\".",
                                     [awards objectAtIndex:0], AL_VALID_1_1);
                STAssertEqualObjects([awards objectAtIndex:1], AL_VALID_1_2,
                                     @"first award in list is \"%@\"; should be \"%@\".",
                                     [awards objectAtIndex:1], AL_VALID_1_2);
            } // if
        } // if
    } // if
} // testAwardListValue


#define BOOLEAN_INVALID_SRC @"ldfv quh"
- (void)testBooleanValue
{
    NSString *src = BOOLEAN_INVALID_SRC;
    STAssertFalse([QEADIFValue isValidBooleanSrc:&src],
                  @"Reported \"%@\" as valid source for ADIF Boolean.", BOOLEAN_INVALID_SRC);
    STAssertTrue(src == BOOLEAN_INVALID_SRC,
                 @"Replaced uncorrectable source \"%@\" with \"%@\".",
                 BOOLEAN_INVALID_SRC, src);
    
    src = ADIF_BOOLEAN_TRUE;
    STAssertTrue([QEADIFValue isValidBooleanSrc:&src],
                 @"Reported \"%@\" as invalid source for ADIF Boolean.", ADIF_BOOLEAN_TRUE);
    STAssertTrue(src == ADIF_BOOLEAN_TRUE,
                 @"Replaced correct source \"%@\" with \"%@\".",
                 ADIF_BOOLEAN_TRUE, src);
    src = ADIF_BOOLEAN_FALSE;
    STAssertTrue([QEADIFValue isValidBooleanSrc:&src],
                 @"Reported \"%@\" as invalid source for ADIF Boolean.", ADIF_BOOLEAN_FALSE);
    STAssertTrue(src == ADIF_BOOLEAN_FALSE,
                 @"Replaced correct source \"%@\" with \"%@\".",
                 ADIF_BOOLEAN_FALSE, src);
    
    src = [ADIF_BOOLEAN_TRUE lowercaseString];
    QEADIFValue *v = [QEADIFValue booleanFromString:src];
    STAssertNotNil(v, @"Could not create Boolean ADIF value from source \"%@\".", src);
    STAssertTrue([[v stringValue] isEqual:ADIF_BOOLEAN_TRUE],
                 @"Boolean from source \"%@\" should have stringValue \"%@\"; value is \"%@\".",
                 src, ADIF_BOOLEAN_TRUE, [v stringValue]);
    STAssertTrue([[v value] boolValue],
                 @"Boolean ADIF value from source \"%@\" should be true; value is \"%@\".",
                 src, [v value]);
    STAssertTrue([v adifType] == qeADIFTypeBoolean,
                 @"Boolean value type code is %d; should be %d",
                 [v adifType], qeADIFTypeBoolean);
    STAssertNil([v enumerationSpec],
                @"enumerationName property must be nil except for enumeration values; name here is \"%@\".",
                [v enumerationSpec]);
    src = [ADIF_BOOLEAN_FALSE lowercaseString];
    v = [QEADIFValue booleanFromString:src];
    STAssertNotNil(v, @"Could not create Boolean ADIF value from source \"%@\".", src);
    STAssertTrue([[v stringValue] isEqual:ADIF_BOOLEAN_FALSE],
                 @"Boolean from source \"%@\" should have stringValue \"%@\"; value is \"%@\".",
                 src, ADIF_BOOLEAN_FALSE, [v stringValue]);
    STAssertNotNil([v value], @"Boolean from source \"%@\" has nil for natural value.",
                   src);
    STAssertFalse([[v value] boolValue],
                  @"Boolean ADIF value from source \"%@\" should be false; value is \"%@\".",
                  src, [v value]);
    STAssertTrue([v adifType] == qeADIFTypeBoolean,
                 @"Boolean value type code is %d; should be %d",
                 [v adifType], qeADIFTypeBoolean);
    STAssertNil([v enumerationSpec],
                @"enumerationName property must be nil except for enumeration values; name here is \"%@\".",
                [v enumerationSpec]);
} // testBooleanValue


#define DIGIT_VALID_SRC_1 @"2"
#define DIGIT_VALID_CHAR_1 '2'
#define DIGIT_VALID_VALUE_1 2
#define DIGIT_INVALID_SRC_1 @"k"
#define DIGIT_INVALID_SRC_2 @"aggle flaggle klabble!"
- (void)testDigitValue
{
    NSString *src = DIGIT_VALID_SRC_1;
    STAssertTrue([QEADIFValue isValidDigitSrc:&src],
                 @"Didn't recognize \"%@\" as valid digit.",
                 DIGIT_VALID_SRC_1);
    STAssertTrue(src == DIGIT_VALID_SRC_1,
                 @"Modified correct source string: was \"%@\", is \"%@\".",
                 DIGIT_VALID_SRC_1);
    
    src = DIGIT_INVALID_SRC_1;
    STAssertFalse([QEADIFValue isValidDigitSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF digit.",
                  DIGIT_INVALID_SRC_1);
    STAssertTrue(DIGIT_INVALID_SRC_1 == src,
                 @"Modified uncorrectable source string: was \"%@\", is \"%@\".",
                 DIGIT_INVALID_SRC_1, src);
    src = DIGIT_INVALID_SRC_2;
    STAssertFalse([QEADIFValue isValidDigitSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF digit.",
                  DIGIT_INVALID_SRC_2);
    STAssertTrue(DIGIT_INVALID_SRC_2 == src,
                 @"Modified uncorrectable source string: was \"%@\", is \"%@\".",
                 DIGIT_INVALID_SRC_2, src);
    
    QEADIFValue *v = [QEADIFValue digitFromString:DIGIT_VALID_SRC_1];
    STAssertNotNil(v,
                   @"Could not create digit value from source \"%@\".", DIGIT_VALID_SRC_1);
    STAssertTrue([[v stringValue] isEqual:DIGIT_VALID_SRC_1],
                 @"Digit value from source \"%@\" has string value \"%@\".",
                 DIGIT_VALID_SRC_1, [v stringValue]);
    STAssertTrue([[v value] integerValue] == DIGIT_VALID_VALUE_1,
                 @"Digit value from source \"%@\" should have value %d; has value %@.",
                 DIGIT_VALID_SRC_1, DIGIT_VALID_VALUE_1, [v value]);
    STAssertTrue([v adifType] == qeADIFTypeDigit,
                 @"Boolean value type code is %d; should be %d",
                 [v adifType], qeADIFTypeDigit);
    STAssertNil([v enumerationSpec],
                @"enumerationName property must be nil except for enumeration values; name here is \"%@\".",
                [v enumerationSpec]);
    
    v = [QEADIFValue digitFromUint:DIGIT_VALID_VALUE_1];
    STAssertNotNil(v,
                   @"Could not create digit value from number %d.", DIGIT_VALID_VALUE_1);
    STAssertTrue([[v stringValue] isEqual:DIGIT_VALID_SRC_1],
                 @"Digit value should have string value \"%@\"; has string value \"%@\".",
                 DIGIT_VALID_SRC_1, [v stringValue]);
    STAssertTrue([[v value] integerValue] == DIGIT_VALID_VALUE_1,
                 @"Digit value from source value %d  has value %@.",
                 DIGIT_VALID_VALUE_1, [v value]);
    STAssertTrue([v adifType] == qeADIFTypeDigit,
                 @"Boolean value type code is %d; should be %d",
                 [v adifType], qeADIFTypeDigit);
    STAssertNil([v enumerationSpec],
                @"enumerationName property must be nil except for enumeration values; name here is \"%@\".",
                [v enumerationSpec]);
    
    v = [QEADIFValue digitFromChar:DIGIT_VALID_CHAR_1];
    STAssertNotNil(v,
                   @"Could not create digit value from character %C.", DIGIT_VALID_CHAR_1);
    STAssertTrue([[v stringValue] isEqual:DIGIT_VALID_SRC_1],
                 @"Digit value should have string value \"%@\"; has string value \"%@\".",
                 DIGIT_VALID_SRC_1, [v stringValue]);
    STAssertTrue([[v value] integerValue] == DIGIT_VALID_VALUE_1,
                 @"Digit value from source character \'%C\'  has value %@.",
                 DIGIT_VALID_CHAR_1, [v value]);
    STAssertTrue([v adifType] == qeADIFTypeDigit,
                 @"Boolean value type code is %d; should be %d",
                 [v adifType], qeADIFTypeDigit);
    STAssertNil([v enumerationSpec],
                @"enumerationName property must be nil except for enumeration values; name here is \"%@\".",
                [v enumerationSpec]);
} // testDigitValue


#define NUMBER_VALID_SRC_1 @"0"
#define NUMBER_VALID_1_DBL ((double) 0.0)
#define NUMBER_VALID_SRC_2 @"1"
#define NUMBER_VALID_SRC_3 @"1234."
#define NUMBER_VALID_SRC_4 @"0.25"
#define NUMBER_VALID_4_DBL ((double) 0.25)
#define NUMBER_VALID_SRC_5 @".00000005"
#define NUMBER_INVALID_SRC_1 @"q"
#define NUMBER_INVALID_SRC_2 @"11111a1"
#define NUMBER_INVALID_SRC_3 @""
#define NUMBER_INVALID_SRC_4 @"123.45.6"
- (void)testNumberValue
{
    NSString *src = NUMBER_VALID_SRC_1;
    STAssertTrue([QEADIFValue isValidNumberSrc:&src],
                 @"Didn't recognize \"%@\" as valid ADIF number.",
                 NUMBER_VALID_SRC_1);
    STAssertTrue(NUMBER_VALID_SRC_1 == src,
                 @"Modified correct source string: was \"%@\", is \"%@\".",
                 NUMBER_VALID_SRC_1, src);
    src = NUMBER_VALID_SRC_2;
    STAssertTrue([QEADIFValue isValidNumberSrc:&src],
                 @"Didn't recognize \"%@\" as valid ADIF number.",
                 NUMBER_VALID_SRC_2);
    STAssertTrue(NUMBER_VALID_SRC_2 == src,
                 @"Modified correct source string: was \"%@\", is \"%@\".",
                 NUMBER_VALID_SRC_2, src);
    src = NUMBER_VALID_SRC_3;
    STAssertTrue([QEADIFValue isValidNumberSrc:&src],
                 @"Didn't recognize \"%@\" as valid ADIF number.",
                 NUMBER_VALID_SRC_3);
    STAssertTrue(NUMBER_VALID_SRC_3 == src,
                 @"Modified correct source string: was \"%@\", is \"%@\".",
                 NUMBER_VALID_SRC_3, src);
    src = NUMBER_VALID_SRC_4;
    STAssertTrue([QEADIFValue isValidNumberSrc:&src],
                 @"Didn't recognize \"%@\" as valid ADIF number.",
                 NUMBER_VALID_SRC_4);
    STAssertTrue(NUMBER_VALID_SRC_4 == src,
                 @"Modified correct source string: was \"%@\", is \"%@\".",
                 NUMBER_VALID_SRC_4, src);
    src = NUMBER_VALID_SRC_5;
    STAssertTrue([QEADIFValue isValidNumberSrc:&src],
                 @"Didn't recognize \"%@\" as valid ADIF number.",
                 NUMBER_VALID_SRC_5);
    STAssertTrue(NUMBER_VALID_SRC_5 == src,
                 @"Modified correct source string: was \"%@\", is \"%@\".",
                 NUMBER_VALID_SRC_5, src);
    
    src = NUMBER_INVALID_SRC_1;
    STAssertFalse([QEADIFValue isValidNumberSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF number.",
                  NUMBER_INVALID_SRC_1);
    STAssertTrue(NUMBER_INVALID_SRC_1 == src,
                 @"Modified uncorrectable source string: was \"%@\", is \"%@\".",
                 NUMBER_INVALID_SRC_1, src);
    src = NUMBER_INVALID_SRC_2;
    STAssertFalse([QEADIFValue isValidNumberSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF number.",
                  NUMBER_INVALID_SRC_2);
    STAssertTrue(NUMBER_INVALID_SRC_2 == src,
                 @"Modified uncorrectable source string: was \"%@\", is \"%@\".",
                 NUMBER_INVALID_SRC_2, src);
    src = NUMBER_INVALID_SRC_3;
    STAssertFalse([QEADIFValue isValidNumberSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF number.",
                  NUMBER_INVALID_SRC_3);
    STAssertTrue(NUMBER_INVALID_SRC_3 == src,
                 @"Modified uncorrectable source string: was \"%@\", is \"%@\".",
                 NUMBER_INVALID_SRC_3, src);
    src = NUMBER_INVALID_SRC_4;
    STAssertFalse([QEADIFValue isValidNumberSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF number.",
                  NUMBER_INVALID_SRC_4);
    STAssertTrue(NUMBER_INVALID_SRC_4 == src,
                 @"Modified uncorrectable source string: was \"%@\", is \"%@\".",
                 NUMBER_INVALID_SRC_4, src);
    
    QEADIFValue *v = [QEADIFValue numberFromString:NUMBER_VALID_SRC_1];
    STAssertNotNil(v, @"Couldn't create ADIF number from string \"%@\".",
                   NUMBER_VALID_SRC_1);
    STAssertTrue([[v stringValue] isEqual:NUMBER_VALID_SRC_1],
                 @"String value of ADIF number is \"%@\"; differs from source \"%@\".",
                 [v stringValue],
                 NUMBER_VALID_SRC_1);
    STAssertTrue([[v value] doubleValue] == NUMBER_VALID_1_DBL,
                 @"Value of ADIF number from source \"%@\" is %f; should be %f.",
                 NUMBER_VALID_SRC_1,
                 NUMBER_VALID_1_DBL,
                 [[v value] doubleValue]);
    v = [QEADIFValue numberFromString:NUMBER_VALID_SRC_4];
    STAssertNotNil(v, @"Couldn't create ADIF number from string \"%@\".",
                   NUMBER_VALID_SRC_4);
    STAssertTrue([[v stringValue] isEqual:NUMBER_VALID_SRC_4],
                 @"String value of ADIF number is \"%@\"; differs from source \"%@\".",
                 [v stringValue],
                 NUMBER_VALID_SRC_4);
    STAssertTrue([[v value] doubleValue] == NUMBER_VALID_4_DBL,
                 @"Value of ADIF number from source \"%@\" is %f; should be %f.",
                 NUMBER_VALID_SRC_4,
                 NUMBER_VALID_4_DBL,
                 [[v value] doubleValue]);
} // testNumberValue


#define CHAR_VALID_SRC_1 @"a"
#define CHAR_VALID_CHAR_1 'a'
#define CHAR_INVALID_SRC_1 @"‡"
#define CHAR_INVALID_CHAR_1 '‡'
#define CHAR_INVALID_SRC_2 @""
#define CHAR_INVALID_SRC_3 @"aa"
- (void)testCharacterValue
{
    NSString *src = CHAR_VALID_SRC_1;
    STAssertTrue([QEADIFValue isValidCharacterSrc:&src],
                 @"Didn't recognize \"%@\" as valid character.",
                 CHAR_VALID_SRC_1);
    STAssertTrue(src == CHAR_VALID_SRC_1,
                 @"Replaced correct source \"%@\" with \"%@\".",
                 CHAR_VALID_SRC_1, src);
    src = CHAR_INVALID_SRC_1;
    STAssertFalse([QEADIFValue isValidCharacterSrc:&src],
                  @"Didn't recognize \"%@\" as invalid character.",
                  CHAR_VALID_SRC_1);
    STAssertTrue(src == CHAR_INVALID_SRC_1,
                 @"Replaced uncorrectable source \"%@\" with \"%@\".",
                 CHAR_VALID_SRC_1, src);
    src = CHAR_INVALID_SRC_2;
    STAssertFalse([QEADIFValue isValidCharacterSrc:&src],
                  @"Didn't recognize \"%@\" as invalid character.",
                  CHAR_INVALID_SRC_2);
    STAssertTrue(src == CHAR_INVALID_SRC_2,
                 @"Replaced uncorrectable source \"%@\" with \"%@\".",
                 CHAR_INVALID_SRC_2, src);
    src = CHAR_INVALID_SRC_3;
    STAssertFalse([QEADIFValue isValidCharacterSrc:&src],
                  @"Didn't recognize \"%@\" as invalid character.",
                  CHAR_INVALID_SRC_3);
    STAssertTrue(src == CHAR_INVALID_SRC_3,
                 @"Replaced uncorrectable source \"%@\" with \"%@\".",
                 CHAR_INVALID_SRC_3, src);
    
    QEADIFValue *v = [QEADIFValue characterFromString:CHAR_VALID_SRC_1];
    STAssertNotNil(v,
                   @"Couldn't create ADIF Character from source \"%@\".",
                   CHAR_VALID_SRC_1);
    STAssertTrue([[v stringValue] isEqual:CHAR_VALID_SRC_1],
                 @"Character's stringValue is \"%@\"; should be \"%@\".",
                 [v stringValue],
                 CHAR_VALID_SRC_1);
    STAssertTrue([[v value] isEqual:CHAR_VALID_SRC_1],
                 @"Character's value is string \"%@\"; should be \"%@\".",
                 [v stringValue],
                 CHAR_VALID_SRC_1);
    STAssertTrue([v adifType] == qeADIFTypeCharacter,
                 @"Type code is %d; should be %d.",
                 [v adifType],
                 qeADIFTypeCharacter);
    v = [QEADIFValue characterFromChar:CHAR_VALID_CHAR_1];
    STAssertNotNil(v,
                   @"Couldn't create ADIF Character from source \"%@\".",
                   CHAR_VALID_SRC_1);
    STAssertTrue([[v stringValue] isEqual:CHAR_VALID_SRC_1],
                 @"Character's stringValue is \"%@\"; should be \"%@\".",
                 [v stringValue],
                 CHAR_VALID_SRC_1);
    STAssertTrue([[v value] isEqual:CHAR_VALID_SRC_1],
                 @"Character's value is string \"%@\"; should be \"%@\".",
                 [v stringValue],
                 CHAR_VALID_SRC_1);    
    STAssertTrue([v adifType] == qeADIFTypeCharacter,
                 @"Type code is %d; should be %d.",
                 [v adifType],
                 qeADIFTypeCharacter);
    v = [QEADIFValue characterFromChar:CHAR_VALID_CHAR_1];
} // testCharacterValue


#define DATE_VALID_SRC_1 @"20090526"
#define DATE_VALID_1_YEAR (2009)
#define DATE_VALID_1_MONTH (5)
#define DATE_VALID_1_DAY (26)
#define DATE_VALID_SRC_2 @"19300101"
#define DATE_CORRECTABLE_SRC @"    20090527  "
#define DATE_CORRECTED_SRC @"20090527"
#define DATE_INVALID_SRC_1 @"19291231"
#define DATE_INVALID_SRC_2 @"090526"
#define DATE_INVALID_SRC_3 @"boogers"
#define DATE_INVALID_SRC_4 @"20090230"
#define DATE_INVALID_SRC_5 @"20091526"
#define DATE_INVALID_SRC_6 @"-20090526"
#define DATE_INVALID_SRC_7 @"21000229"
- (void)testDateValue
{
    NSString *src = DATE_VALID_SRC_1;
    STAssertTrue([QEADIFValue isValidDateSrc:&src],
                 @"Didn't recognize \"%@\" as valid ADIF date.",
                 DATE_VALID_SRC_1);
    STAssertTrue(src == DATE_VALID_SRC_1,
                 @"Modified correct date source: was \"%@\", is \"%@\"."
                 DATE_VALID_SRC_1, src);
    src = DATE_VALID_SRC_2;
    STAssertTrue([QEADIFValue isValidDateSrc:&src],
                 @"Didn't recognize \"%@\" as valid ADIF date.",
                 DATE_VALID_SRC_2);
    STAssertTrue(src == DATE_VALID_SRC_2,
                 @"Modified correct date source: was \"%@\", is \"%@\"."
                 DATE_VALID_SRC_2, src);
    
    src = DATE_CORRECTABLE_SRC;
    STAssertTrue([QEADIFValue isValidDateSrc:&src],
                 @"Didn't recognize \"%@\" as valid ADIF date.",
                 DATE_CORRECTABLE_SRC);
    STAssertTrue([src isEqual:DATE_CORRECTED_SRC],
                 @"Failed to correct date source: was \"%@\", is \"%@\"; should be \"%@\"."
                 DATE_CORRECTABLE_SRC, src, DATE_CORRECTED_SRC);
    
    src = DATE_INVALID_SRC_1;
    STAssertFalse([QEADIFValue isValidDateSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF date.",
                  DATE_INVALID_SRC_1);
    STAssertTrue(src == DATE_INVALID_SRC_1,
                 @"Modified uncorrectable date source string: was \"%@\", is \"%@\"."
                 DATE_INVALID_SRC_1, src);
    src = DATE_INVALID_SRC_2;
    STAssertFalse([QEADIFValue isValidDateSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF date.",
                  DATE_INVALID_SRC_2);
    STAssertTrue(src == DATE_INVALID_SRC_2,
                 @"Modified uncorrectable date source string: was \"%@\", is \"%@\"."
                 DATE_INVALID_SRC_2, src);
    src = DATE_INVALID_SRC_3;
    STAssertFalse([QEADIFValue isValidDateSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF date.",
                  DATE_INVALID_SRC_3);
    STAssertTrue(src == DATE_INVALID_SRC_3,
                 @"Modified uncorrectable date source string: was \"%@\", is \"%@\"."
                 DATE_INVALID_SRC_3, src);
    src = DATE_INVALID_SRC_4;
    STAssertFalse([QEADIFValue isValidDateSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF date.",
                  DATE_INVALID_SRC_4);
    STAssertTrue(src == DATE_INVALID_SRC_4,
                 @"Modified uncorrectable date source string: was \"%@\", is \"%@\"."
                 DATE_INVALID_SRC_4, src);
    src = DATE_INVALID_SRC_5;
    STAssertFalse([QEADIFValue isValidDateSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF date.",
                  DATE_INVALID_SRC_5);
    STAssertTrue(src == DATE_INVALID_SRC_5,
                 @"Modified uncorrectable date source string: was \"%@\", is \"%@\"."
                 DATE_INVALID_SRC_5, src);
    src = DATE_INVALID_SRC_6;
    STAssertFalse([QEADIFValue isValidDateSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF date.",
                  DATE_INVALID_SRC_6);
    STAssertTrue(src == DATE_INVALID_SRC_6,
                 @"Modified uncorrectable date source string: was \"%@\", is \"%@\"."
                 DATE_INVALID_SRC_6, src);
    src = DATE_INVALID_SRC_7;
    STAssertFalse([QEADIFValue isValidDateSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF date.",
                  DATE_INVALID_SRC_7);
    STAssertTrue(src == DATE_INVALID_SRC_7,
                 @"Modified uncorrectable date source string: was \"%@\", is \"%@\"."
                 DATE_INVALID_SRC_7, src);
    
    QEADIFValue *v = [QEADIFValue dateFromString:DATE_VALID_SRC_1];
    STAssertNotNil(v,
                   @"Couldn't create ADIF Date value from valid string \"%@\".",
                   DATE_VALID_SRC_1);
    if (nil != v) {
        STAssertEqualObjects([v stringValue], DATE_VALID_SRC_1,
                             @"String value of ADIF date created from source \"%@\" is \"%@\".",
                             DATE_VALID_SRC_1, [v stringValue]);
        NSDateComponents *comp = [v value];
        STAssertNotNil(comp,
                       @"Failed to return non-nil components from date created from source \"%@\".",
                       DATE_VALID_SRC_1);
        if (nil != comp) {
            STAssertEquals(DATE_VALID_1_YEAR, [comp year],
                           @"In date from source \"%@\", year should be %4d; year is %4d."
                           DATE_VALID_SRC_1, DATE_VALID_1_YEAR, [comp year]);
            STAssertEquals(DATE_VALID_1_MONTH, [comp month],
                           @"In date from source \"%@\", month should be %02d; month is %02d."
                           DATE_VALID_SRC_1, DATE_VALID_1_MONTH, [comp year]);
            STAssertEquals(DATE_VALID_1_DAY, [comp day],
                           @"In date from source \"%@\", day should be %02d; day is %02d."
                           DATE_VALID_SRC_1, DATE_VALID_1_DAY, [comp year]);
            STAssertTrue([v adifType] == qeADIFTypeDate,
                         @"Date value type code is %d; should be %d",
                         [v adifType], qeADIFTypeDate);
        } // if
    } // if
    
    NSDateComponents *c = [[NSDateComponents alloc] init];
    [c setYear:DATE_VALID_1_YEAR];
    [c setMonth:DATE_VALID_1_MONTH];
    [c setDay:DATE_VALID_1_DAY];
    v = [QEADIFValue dateFromComponents:c];
    STAssertNotNil(v, @"Could not create date from components.");
    if (nil != v) {
        STAssertEqualObjects([v stringValue], DATE_VALID_SRC_1,
                             @"String value of ADIF date created from components is \"%@\".",
                             [v stringValue]);
        NSDateComponents *comp = [v value];
        STAssertNotNil(comp,
                       @"Failed to return non-nil components from date created from components.");
        if (nil != comp) {
            STAssertEquals(DATE_VALID_1_YEAR, [comp year],
                           @"In date from components, year should be %4d; year is %4d.",
                           DATE_VALID_1_YEAR, [comp year]);
            STAssertEquals(DATE_VALID_1_MONTH, [comp month],
                           @"In date from components, month should be %02d; month is %02d.",
                           DATE_VALID_1_MONTH, [comp year]);
            STAssertEquals(DATE_VALID_1_DAY, [comp day],
                           @"In date from components, day should be %02d; day is %02d.",
                           DATE_VALID_1_DAY, [comp year]);
            STAssertTrue([v adifType] == qeADIFTypeDate,
                         @"Date value type code is %d; should be %d",
                         [v adifType], qeADIFTypeDate);
        } // if
    } // if 
} // testDateValue


#define TIME_VALID_SRC_1 @"0000"
#define TIME_VALID_SRC_2 @"000000"
#define TIME_VALID_SRC_3 @"2359"
#define TIME_CORRECTABLE_SRC_1 @"  235959 "
#define TIME_CORRECTED_SRC_1 @"235959"
#define TIME_VALID_4_HOURS (23)
#define TIME_VALID_4_MINUTES (59)
#define TIME_VALID_4_SECONDS (59)
#define TIME_INVALID_SRC_1 @"11"
#define TIME_INVALID_SRC_2 @"345"
#define TIME_INVALID_SRC_3 @"23456"
#define TIME_INVALID_SRC_4 @"1234567"
#define TIME_INVALID_SRC_5 @"boogers"
#define TIME_INVALID_SRC_6 @"2360"
#define TIME_INVALID_SRC_7 @"235960"
#define TIME_INVALID_SRC_8 @"2400"
#define TIME_INVALID_SRC_9 @"240000"
- (void)testTimeValue
{
    NSArray *valid = [NSArray arrayWithObjects: TIME_VALID_SRC_1, TIME_VALID_SRC_2, 
                      TIME_VALID_SRC_3, nil];
    for (NSString *base in valid) {
        NSString *src = base;
        STAssertTrue([QEADIFValue isValidTimeSrc:&src],
                     @"Didn't recognize \"%@\" as valid ADIF Time string.",
                     base);
        STAssertTrue(src == base,
                     @"Replaced correct Time source \"%@\" with \"%@\".",
                     base, src);
    } // for
    
    NSArray *invalid = [NSArray arrayWithObjects: TIME_INVALID_SRC_1, TIME_INVALID_SRC_2,
                        TIME_INVALID_SRC_3, TIME_INVALID_SRC_4, 
                        TIME_INVALID_SRC_5, TIME_INVALID_SRC_6, 
                        TIME_INVALID_SRC_7, TIME_INVALID_SRC_8, 
                        TIME_INVALID_SRC_9, nil];
    for (NSString *base in invalid) {
        NSString *src = base;
        STAssertFalse([QEADIFValue isValidTimeSrc:&src],
                      @"Didn't recognize \"%@\" as invalid ADIF Time string.",
                      base);
        STAssertTrue(src == base,
                     @"Replaced uncorrectable Time source \"%@\" with \"%@\".",
                     base, src);
    } // for
    
    QEADIFValue *v = [QEADIFValue timeFromString:TIME_CORRECTABLE_SRC_1];
    STAssertNotNil(v,
                   @"Couldn't create ADIF Time value from valid string \"%@\".",
                   TIME_CORRECTABLE_SRC_1);
    if (nil != v) {
        STAssertEqualObjects([v stringValue], TIME_CORRECTED_SRC_1,
                             @"String value of ADIF Time created from source \"%@\" is \"%@\"; should be \"%@\".",
                             TIME_CORRECTABLE_SRC_1, [v stringValue], TIME_CORRECTED_SRC_1);
        NSDateComponents *comp = [v value];
        STAssertNotNil(comp,
                       @"Failed to return non-nil components from Time created from source \"%@\".",
                       TIME_CORRECTABLE_SRC_1);
        if (nil != comp) {
            STAssertEquals(TIME_VALID_4_HOURS, [comp hour],
                           @"In Time from source \"%@\", hour should be %2d; hour is %2d."
                           TIME_CORRECTABLE_SRC_1, TIME_VALID_4_HOURS, [comp hour]);
            STAssertEquals(TIME_VALID_4_MINUTES, [comp minute],
                           @"In Time from source \"%@\", minutes should be %02d; minutes is %02d."
                           TIME_CORRECTABLE_SRC_1, TIME_VALID_4_MINUTES, [comp minute]);
            STAssertEquals(TIME_VALID_4_SECONDS, [comp second],
                           @"In Time from source \"%@\", seconds should be %02d; seconds is %02d."
                           TIME_CORRECTABLE_SRC_1, TIME_VALID_4_SECONDS, [comp second]);
            STAssertTrue([v adifType] == qeADIFTypeTime,
                         @"Time value type code is %d; should be %d",
                         [v adifType], qeADIFTypeTime);
        } // if
    } // if 
    
    NSDateComponents *c = [[NSDateComponents alloc] init];
    [c setHour:TIME_VALID_4_HOURS];
    [c setMinute:TIME_VALID_4_MINUTES];
    [c setSecond:TIME_VALID_4_SECONDS];
    v = [QEADIFValue timeFromComponents:c];
    [c release];
    STAssertNotNil(v, @"Could not create Time from components.");
    if (nil != v) {
        STAssertEqualObjects([v stringValue], TIME_CORRECTED_SRC_1,
                             @"String value of ADIF Time created from components is \"%@\".",
                             [v stringValue]);
        NSDateComponents *comp = [v value];
        STAssertNotNil(comp,
                       @"Failed to return non-nil components from Time created from components.");
        if (nil != comp) {
            STAssertEquals(TIME_VALID_4_HOURS, [comp hour],
                           @"In Time from components, hour should be %2d; hour is %2d.",
                           TIME_VALID_4_HOURS, [comp hour]);
            STAssertEquals(TIME_VALID_4_MINUTES, [comp minute],
                           @"In Time from components, minutes should be %02d; minutes is %02d.",
                           TIME_VALID_4_MINUTES, [comp minute]);
            STAssertEquals(TIME_VALID_4_SECONDS, [comp second],
                           @"In Time from components, seconds should be %02d; seconds is %02d.",
                           TIME_VALID_4_SECONDS, [comp second]);
            STAssertTrue([v adifType] == qeADIFTypeTime,
                         @"Date value type code is %d; should be %d",
                         [v adifType], qeADIFTypeTime);
        } // if
    } // if
} // testTimeValue


#define STRING_VALID_SRC_1 @"abcdefghijklmnop"
// double dagger character is not in ASCII range, so it's illegal in ADIF
#define STRING_INVALID_SRC_1 @"abcdef‡abcdef"
// ADIF forbids non-printing ASCII chars
#define STRING_INVALID_SRC_2 @"abcdef\r\nabcdef"
- (void)testStringValue
{
    NSString *src = STRING_VALID_SRC_1;
    STAssertTrue([QEADIFValue isValidStringSrc:&src],
                 @"Didn't recognize \"%@\" as valid ADIF string.",
                 STRING_VALID_SRC_1);
    STAssertTrue(src == STRING_VALID_SRC_1,
                 @"Modified correct source string: was \"%@\", is \"%@\"."
                 STRING_VALID_SRC_1, src);
    
    src = STRING_INVALID_SRC_1;
    STAssertFalse([QEADIFValue isValidStringSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF string.",
                  STRING_INVALID_SRC_1);
    STAssertTrue(src == STRING_INVALID_SRC_1,
                 @"Modified uncorrectable source string: was \"%@\", is \"%@\"."
                 STRING_INVALID_SRC_1, src);
    
    src = STRING_INVALID_SRC_2;
    STAssertFalse([QEADIFValue isValidStringSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF string.",
                  STRING_INVALID_SRC_2);
    STAssertTrue(src == STRING_INVALID_SRC_2,
                 @"Modified uncorrectable source string: was \"%@\", is \"%@\"."
                 STRING_INVALID_SRC_2, src);
    
    
    QEADIFValue *v = [QEADIFValue stringFromString:STRING_VALID_SRC_1];
    STAssertNotNil(v, @"Could not create string value from string \"%@\"",
                   STRING_VALID_SRC_1);
    STAssertTrue([[v stringValue] isEqual:STRING_VALID_SRC_1],
                 @"String value has stringValue \"%@\" that differs from source \"%@\".",
                 [v stringValue], STRING_VALID_SRC_1);
    STAssertTrue([[v stringValue] isEqual:[v value]],
                 @"String value has stringValue \"%@\" that differs from natural value \"%@\".",
                 [v stringValue], [v value]);
    STAssertTrue([v adifType] == qeADIFTypeString,
                 @"String value type code is %d; should be %d",
                 [v adifType], qeADIFTypeString);
    STAssertNil([v enumerationSpec],
                @"enumerationName property must be nil except for enumeration values; name here is \"%@\".",
                [v enumerationSpec]);
} // testStringValue


#define MLSTRING_VALID_SRC_1 @"abcdefghijklmnop"
#define MLSTRING_VALID_SRC_2 @"abcdefgh\r\nijklmnop"
#define MLSTRING_VALID_SRC_3 @"\r\nabcdefghijklmnop"
#define MLSTRING_VALID_SRC_4 @"abcdefghijklmnop\r\n"
// double dagger character is not in ASCII range, so it's illegal in ADIF
#define MLSTRING_INVALID_SRC_1 @"abcdef‡abcdef"
// ADIF forbids non-printing ASCII chars other than CRLF sequence
#define MLSTRING_INVALID_SRC_2 @"abcdef\nabcdef"
#define MLSTRING_INVALID_SRC_3 @"abcdef\rabcdef"
#define MLSTRING_INVALID_SRC_4 @"abcdef\tabcdef"
- (void)testMLStringValue
{
    NSString *src = MLSTRING_VALID_SRC_1;
    STAssertTrue([QEADIFValue isValidMultilineStringSrc:&src],
                 @"Didn't recognize \"%@\" as valid ADIF multiline string.",
                 MLSTRING_VALID_SRC_1);
    STAssertTrue(src == MLSTRING_VALID_SRC_1,
                 @"Modified correct source string: was \"%@\", is \"%@\"."
                 MLSTRING_VALID_SRC_1, src);
    src = MLSTRING_VALID_SRC_2;
    STAssertTrue([QEADIFValue isValidMultilineStringSrc:&src],
                 @"Didn't recognize \"%@\" as valid ADIF multiline string.",
                 MLSTRING_VALID_SRC_2);
    STAssertTrue(src == MLSTRING_VALID_SRC_2,
                 @"Modified correct source string: was \"%@\", is \"%@\"."
                 MLSTRING_VALID_SRC_2, src);
    src = MLSTRING_VALID_SRC_3;
    STAssertTrue([QEADIFValue isValidMultilineStringSrc:&src],
                 @"Didn't recognize \"%@\" as valid ADIF multiline string.",
                 MLSTRING_VALID_SRC_3);
    STAssertTrue(src == MLSTRING_VALID_SRC_3,
                 @"Modified correct source string: was \"%@\", is \"%@\"."
                 MLSTRING_VALID_SRC_3, src);
    src = MLSTRING_VALID_SRC_4;
    STAssertTrue([QEADIFValue isValidMultilineStringSrc:&src],
                 @"Didn't recognize \"%@\" as valid ADIF multiline string.",
                 MLSTRING_VALID_SRC_4);
    STAssertTrue(src == MLSTRING_VALID_SRC_4,
                 @"Modified correct source string: was \"%@\", is \"%@\"."
                 MLSTRING_VALID_SRC_4, src);
    
    src = MLSTRING_INVALID_SRC_1;
    STAssertFalse([QEADIFValue isValidMultilineStringSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF multiline string.",
                  MLSTRING_INVALID_SRC_1);
    STAssertTrue(src == MLSTRING_INVALID_SRC_1,
                 @"Modified uncorrectable source string: was \"%@\", is \"%@\"."
                 MLSTRING_INVALID_SRC_1, src);
    src = MLSTRING_INVALID_SRC_2;
    STAssertFalse([QEADIFValue isValidMultilineStringSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF multiline string.",
                  MLSTRING_INVALID_SRC_2);
    STAssertTrue(src == MLSTRING_INVALID_SRC_2,
                 @"Modified uncorrectable source string: was \"%@\", is \"%@\"."
                 MLSTRING_INVALID_SRC_2, src);
    src = MLSTRING_INVALID_SRC_3;
    STAssertFalse([QEADIFValue isValidMultilineStringSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF multiline string.",
                  MLSTRING_INVALID_SRC_3);
    STAssertTrue(src == MLSTRING_INVALID_SRC_3,
                 @"Modified uncorrectable source string: was \"%@\", is \"%@\"."
                 MLSTRING_INVALID_SRC_3, src);
    src = MLSTRING_INVALID_SRC_4;
    STAssertFalse([QEADIFValue isValidMultilineStringSrc:&src],
                  @"Didn't recognize \"%@\" as invalid ADIF multiline string.",
                  MLSTRING_INVALID_SRC_4);
    STAssertTrue(src == MLSTRING_INVALID_SRC_4,
                 @"Modified uncorrectable source string: was \"%@\", is \"%@\"."
                 MLSTRING_INVALID_SRC_4, src);
    
    
    QEADIFValue *v = [QEADIFValue multilineStringFromString:MLSTRING_VALID_SRC_1];
    STAssertNotNil(v, @"Could not create multiline string value from string \"%@\"",
                   MLSTRING_VALID_SRC_1);
    STAssertTrue([[v stringValue] isEqual:MLSTRING_VALID_SRC_1],
                 @"Multiline string value has stringValue \"%@\" that differs from source \"%@\".",
                 [v stringValue], MLSTRING_VALID_SRC_1);
    STAssertTrue([[v stringValue] isEqual:[v value]],
                 @"Multiline string value has stringValue \"%@\" that differs from natural value \"%@\".",
                 [v stringValue], [v value]);
    STAssertTrue([v adifType] == qeADIFTypeMultilineString,
                 @"Multiline string value type code is %d; should be %d",
                 [v adifType], qeADIFTypeMultilineString);
    STAssertNil([v enumerationSpec],
                @"enumerationName property must be nil except for enumeration values; name here is \"%@\".",
                [v enumerationSpec]);
} // testMLStringValue

 
#define ENUM_INVALID_NAME_1 @"boogers"
#define ENUM_VALID_SRC_1 @"EPA"
#define ENUM_CORRECTABLE_SRC_1 @"  nli "
#define ENUM_CORRECTED_SRC_1 @"NLI"
#define ENUM_INVALID_SRC_1 @"xyz"
- (void)testEnumerationValue
{
    NSString *src = ENUM_VALID_SRC_1;
    STAssertFalse([QEADIFValue isValidValueSrc:&src fromEnumeration:ENUM_INVALID_NAME_1],
                  @"Accepted \"%@\" as legal enumeration name.",
                  ENUM_INVALID_NAME_1);
    STAssertEqualObjects(src, ENUM_VALID_SRC_1,
                         @"Despite invalid enumeration name, replaced source \"%@\" with \"%@\".",
                         ENUM_VALID_SRC_1,
                         src);
    
    src = ENUM_VALID_SRC_1;
    STAssertTrue([QEADIFValue isValidValueSrc:&src fromEnumeration:qeADIFEnumARRLSection],
                 @"Failed to recognize \"%@\" as valid ARRL section.",
                 ENUM_VALID_SRC_1);
    STAssertEqualObjects(src, ENUM_VALID_SRC_1,
                         @"Replaced correct source \"%@\" with \"%@\".",
                         ENUM_VALID_SRC_1,
                         src);
    
    src = ENUM_CORRECTABLE_SRC_1;
    STAssertTrue([QEADIFValue isValidValueSrc:&src fromEnumeration:qeADIFEnumARRLSection],
                 @"Failed to recognize \"%@\" as valid ARRL section.",
                 ENUM_VALID_SRC_1);
    STAssertEqualObjects(src, ENUM_CORRECTED_SRC_1,
                         @"Failed to correct source \"%@\" to \"%@\"; src is now \"%@\".",
                         ENUM_CORRECTABLE_SRC_1,
                         ENUM_CORRECTED_SRC_1,
                         src);
    
    src = ENUM_INVALID_SRC_1;
    STAssertFalse([QEADIFValue isValidValueSrc:&src fromEnumeration:qeADIFEnumARRLSection],
                  @"Failed to recognize \"%@\" as invalid ARRL section.",
                  ENUM_INVALID_SRC_1);
    STAssertEqualObjects(src, ENUM_INVALID_SRC_1,
                         @"Replaced uncorrectable source \"%@\" with \"%@\".",
                         ENUM_INVALID_SRC_1,
                         src);
    
    src = ENUM_VALID_SRC_1;
    QEADIFValue *v = [QEADIFValue valueFromString:src
                                  fromEnumeration:qeADIFEnumARRLSection];
    STAssertNotNil(v,
                   @"Could not create Enumeration value from enumeration \"%@\" with value \"%@\".",
                   qeADIFEnumARRLSection,
                   ENUM_VALID_SRC_1);
    if (nil != v) {
        STAssertEqualObjects([v stringValue], ENUM_VALID_SRC_1,
                             @"String value of ADIF Value is \"%@\"; should be \"%@\".",
                             [v stringValue], ENUM_VALID_SRC_1);
        STAssertEqualObjects([v value], ENUM_VALID_SRC_1,
                             @"Natural value of ADIF Value is \"%@\"; should be \"%@\".",
                             [v value], ENUM_VALID_SRC_1);
        STAssertNotNil([v enumerationSpec],
                       @"Enumeration value has nil enumeration name.");
        if (nil != [v enumerationSpec]) {
            STAssertEqualObjects([v enumerationSpec], qeADIFEnumARRLSection,
                                 @"Enumeration name is \"%@\"; should be \"%@\".",
                                 [v enumerationSpec], qeADIFEnumARRLSection);
        } // if
        STAssertEquals([v adifType], qeADIFTypeEnumeration,
                       @"Type code for enumeration is %d; should be %d.",
                       [v adifType], qeADIFTypeEnumeration);
    } // if
} // testEnumerationValue


#define LOCATION_CORRECTABLE_SRC_1 @"e143 41.099"
#define LOCATION_CORRECTED_SRC_1 @"E143 41.099"
#define LOCATION_VALID_1_DIRECTION 'E'
#define LOCATION_VALID_1_DEGREES (143)
#define LOCATION_VALID_1_MINUTES (41.099)
#define LOCATION_VALID_1_MINUTES_SRC @"41.099"
- (void)testLocationValue
{
    // This is tested pretty thoroughly in connection with the QEADIFLocation
    // class, so only a creation test happens here.
    QEADIFLocation *loc = [[QEADIFLocation alloc] initWithScalarsDirection:LOCATION_VALID_1_DIRECTION
                                                                   degrees:LOCATION_VALID_1_DEGREES
                                                                   minutes:LOCATION_VALID_1_MINUTES];
    QEADIFValue *v = [QEADIFValue locationFromString:LOCATION_CORRECTABLE_SRC_1];
    STAssertNotNil(v,
                   @"Could not create Location value from source \"%@\".",
                   LOCATION_CORRECTABLE_SRC_1);
    if (nil != v) {
        STAssertEqualObjects([v stringValue], LOCATION_CORRECTED_SRC_1,
                             @"String value for location from source \"%@\" should be \"%@\"; is \"%@\".",
                             LOCATION_CORRECTABLE_SRC_1,
                             LOCATION_CORRECTED_SRC_1,
                             [v stringValue]);
        STAssertEqualObjects([v value], loc,
                             @"Value for location from source \"%@\" should be \"%@\"; is \"%@\".",
                             LOCATION_CORRECTABLE_SRC_1,
                             [loc description], [[v value] description]);
        STAssertTrue([v adifType] == qeADIFTypeLocation,
                     @"Location value type code is %d; should be %d",
                     [v adifType], qeADIFTypeLocation);
        STAssertNil([v enumerationSpec],
                    @"enumerationName property must be nil except for enumeration values; name here is \"%@\".",
                    [v enumerationSpec]);
    } // if
    
    v = [QEADIFValue locationFromLocation:loc];
    STAssertNotNil(v,
                   @"Could not create Location value from QEADIFLocation object \"%@\".",
                   [loc description]);
    if (nil != v) {
        STAssertEqualObjects([v stringValue], LOCATION_CORRECTED_SRC_1,
                             @"String value for location from object \"%@\" should be \"%@\"; is \"%@\".",
                             loc,
                             LOCATION_CORRECTED_SRC_1,
                             [v stringValue]);
        STAssertEqualObjects([v value], loc,
                             @"Value for location from source \"%@\" should be \"%@\"; is \"%@\".",
                             loc,
                             [loc description], 
                             [[v value] description]);
        STAssertTrue([v adifType] == qeADIFTypeLocation,
                     @"Location value type code is %d; should be %d",
                     [v adifType], qeADIFTypeLocation);
        STAssertNil([v enumerationSpec],
                    @"enumerationName property must be nil except for enumeration values; name here is \"%@\".",
                    [v enumerationSpec]);
    } // if
    [loc release];
} // testLocationValue
@end
